{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# Unit testing framework\n",
    "\n",
    "## 26.4  unittest — Unit testing framework\n",
    "\n",
    "https://docs.python.org/3/library/unittest.html\n",
    "\n",
    "The unittest unit testing framework was originally inspired by **JUnit** \n",
    "\n",
    "and has a similar flavor as major unit testing frameworks in other languages. \n",
    "\n",
    "It supports test automation, sharing of setup and shutdown code for tests, aggregation of tests into collections, and independence of the tests from the reporting framework.\n",
    "\n",
    "To achieve this, **unittest** supports some important concepts in an object-oriented way:\n",
    "\n",
    "* <b>test fixture</b>: \n",
    "\n",
    "  * A test fixture represents the **preparation** needed to perform one or more tests, and any associate **cleanup actions**. \n",
    "  \n",
    "  This may involve, for example, creating temporary or proxy databases, directories, or starting a server process.\n",
    "\n",
    "\n",
    "* <b>test case</b>: \n",
    "\n",
    "   * A test case is the **individual** unit of testing. \n",
    "   \n",
    "   It checks for a **specific** response to a **particular** set of inputs.\n",
    "   \n",
    "**unittest** provides a base class,　**TestCase**, which may be used to create new test cases.\n",
    "\n",
    "\n",
    "* <b>test suite</b>: \n",
    "\n",
    "  * A test suite is a **collection** of test cases, test suites, or both.\n",
    "  \n",
    "  It is used to **aggregate tests** that should be executed together.\n",
    "\n",
    "\n",
    "* <b>test runner</b>: \n",
    "\n",
    "  * A test runner is a component which orchestrates the execution of tests and provides the outcome to the user. \n",
    "  \n",
    "  The runner may use a graphical interface, a textual interface, or return a special value to indicate the results of executing the tests\n",
    "\n",
    "\n",
    "## Basic Test Structure\n",
    "\n",
    "Tests, as defined by <b>unittest</b>, have two parts: \n",
    "\n",
    "* <b>code to manage test “fixtures”</b>\n",
    "\n",
    "* <b>the test itself</b>. \n",
    "\n",
    "**Individual tests** are created by \n",
    "\n",
    "* subclassing **TestCase** \n",
    "\n",
    "* overriding or adding appropriate methods \n",
    "\n",
    "For example: \n",
    "\n",
    "* **unittest_simple.py**        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./code/unittest/unittest_simple.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/unittest_simple.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class SimplisticTest(unittest.TestCase):\n",
    "\n",
    "    def test_true(self):\n",
    "        self.assertTrue(True)\n",
    " \n",
    "    def test(self):\n",
    "        self.assertTrue(True)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this case, the <b>SimplisticTest</b> have \n",
    "\n",
    "* <b>test_true()</b> \n",
    "\n",
    "* <b>test()</b>\n",
    "\n",
    "methods would <b>fail if True is ever False</b>.\n",
    "\n",
    "The methods are defined with name \n",
    "\n",
    "* **start** with the letters **test**. \n",
    "\n",
    "This naming convention informs the <b>test runner</b> about which methods represent tests.\n",
    "\n",
    "## Running Tests\n",
    "\n",
    "The easiest way to run unittest tests is to include:\n",
    "```python\n",
    "    if __name__ == '__main__':\n",
    "        unittest.main()\n",
    "```\n",
    "at the bottom of each test file, \n",
    "\n",
    "then simply run the script directly from the **command line**:\n",
    "```    \n",
    "   >python unittest_simple.py\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!python ./code/unittest/unittest_simple.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_simple.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "includes \n",
    "\n",
    "* <b>a status indicator for each test</b> \n",
    "\n",
    "   * **”.”** on the first line of output means that a test <b>passed<b>\n",
    "\n",
    "* <b>the amount of time the tests took</b>, \n",
    "\n",
    "\n",
    "For **more** detailed test</b> results, \n",
    "\n",
    "**-v** option:\n",
    "\n",
    "```\n",
    ">python unittest_simple.py -v\n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_simple.py -v"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can run tests with more detailed information by passing in the verbosity argument:\n",
    "```python\n",
    "unittest.main(verbosity=2)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./code/unittest/unittest_simple_more_detailed_information.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/unittest_simple_more_detailed_information.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class SimplisticTest(unittest.TestCase):\n",
    "\n",
    "    def test_true(self):\n",
    "        self.assertTrue(True)\n",
    " \n",
    "    def test(self):\n",
    "        self.assertTrue(True)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main(verbosity=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_simple_more_detailed_information.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test Outcomes\n",
    "\n",
    "Tests have 3 possible outcomes, described in the table below.\n",
    "\n",
    "| Outcome  |  Mark |  Describe  |\n",
    "|:--------:|----------:|--------:|\n",
    "|  ok    | **.** | The test passes |\n",
    "|  FAIL    | **F** | The test does not pass, and raises an **AssertionError** exception |\n",
    "|  ERROR  |  **E** |The test raises an **exception** other than `AssertionError`.|\n",
    "\n",
    "\n",
    "For Example: \n",
    "\n",
    "* **unittest_outcomes.py**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./code/unittest/unittest_outcomes.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/unittest_outcomes.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class OutcomesTest(unittest.TestCase):\n",
    "\n",
    "    #   ok\n",
    "    def test_Pass(self):\n",
    "        return\n",
    "\n",
    "    # FAIL\n",
    "    def test_Fail(self):\n",
    "        # AssertionError exception.\n",
    "        self.assertFalse(True)\n",
    "        \n",
    "    # ERROR\n",
    "    def test_Error(self):\n",
    "        # raises an exception other than AssertionError\n",
    "        raise RuntimeError('Test error!')\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_outcomes.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**When a test fails or generates an error** \n",
    "\n",
    "the **traceback** is included in the output.\n",
    "In the example above, \n",
    "\n",
    "<b>testFail()</b> fails \n",
    "\n",
    "the traceback <b>shows the line</b> with the failure code.\n",
    "\n",
    "It is up to the person reading the test output to look at the code to figure out the semantic meaning of the failed test, though. \n",
    "\n",
    "#### fail with message\n",
    "\n",
    "To make it <b>easier to understand the nature of a test failure</b>,\n",
    "\n",
    "the <b>fail*() and assert*()</b> methods all accept an argument <b>msg</b>,\n",
    "\n",
    "which can be used to produce <b>a more detailed error message</b>\n",
    "\n",
    "Example: \n",
    "\n",
    "* **unittest_failwithmessage.py**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./code/unittest/unittest_failwithmessage.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/unittest_failwithmessage.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class FailureMessageTest(unittest.TestCase):\n",
    "\n",
    "    def test_Fail(self):\n",
    "        self.assertFalse(True,'failure message goes here')\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_failwithmessage.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Asserting Truth\n",
    "\n",
    "Most tests assert the truth of some condition. There are a few different ways to write truth-checking tests, depending on the perspective of the test author and the desired outcome of the code being tested. \n",
    "\n",
    "If the code produces a value which can be evaluated as <b>true</b>, the methods <b>assertTrue()</b>  should be used.\n",
    "\n",
    "If the code produces a <b>false</b> value, the methods <b>assertFalse()</b> make more sense."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing ./code/unittest/unittest_true.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/unittest_true.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class TruthTest(unittest.TestCase):\n",
    "\n",
    "    def testAssertTrue(self):\n",
    "        self.assertTrue(True)\n",
    "\n",
    "    def test_AssertFalse(self):\n",
    "        self.assertFalse(False)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_true.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Testing Equality\n",
    "\n",
    "As a special case, `unittest` includes methods for testing <b>the equality of two values</b>.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%file ./code/unittest/unittest_equality.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class EqualityTest(unittest.TestCase):\n",
    "\n",
    "    def test_Equal(self):\n",
    "        self.assertEqual(1, 3)\n",
    "\n",
    "    def test_NotEqual(self):\n",
    "        self.assertNotEqual(2, 1)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "F.\n",
      "======================================================================\n",
      "FAIL: test_Equal (__main__.EqualityTest)\n",
      "----------------------------------------------------------------------\n",
      "Traceback (most recent call last):\n",
      "  File \"J:\\SEU\\SEE\\PySEE\\home\\notebook\\code\\unittest\\unittest_equality.py\", line 7, in test_Equal\n",
      "    self.assertEqual(1, 3)\n",
      "AssertionError: 1 != 3\n",
      "\n",
      "----------------------------------------------------------------------\n",
      "Ran 2 tests in 0.002s\n",
      "\n",
      "FAILED (failures=1)\n"
     ]
    },
    {
     "ename": "SystemExit",
     "evalue": "True",
     "output_type": "error",
     "traceback": [
      "An exception has occurred, use %tb to see the full traceback.\n",
      "\u001b[1;31mSystemExit\u001b[0m\u001b[1;31m:\u001b[0m True\n"
     ]
    }
   ],
   "source": [
    "%run ./code/unittest/unittest_equality.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These special tests are handy, since the values being <b>compared appear in the failure message</b> when a test fails."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%file ./code/unittest/unittest_notequal.py\n",
    "import unittest\n",
    "\n",
    "class InequalityTest(unittest.TestCase):\n",
    "\n",
    "    def test_Equal(self):\n",
    "        self.assertNotEqual(1, 1)\n",
    "\n",
    "    def test_NotEqual(self):\n",
    "        self.assertEqual(2, 1)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_notequal.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Almost Equal?\n",
    "\n",
    "In addition to strict equality, it is possible to test for\n",
    "\n",
    "**near equality of floating point numbers** using\n",
    "\n",
    "* assertNotAlmostEqual()\n",
    "\n",
    "* assertAlmostEqual()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./code/unittest/unittest_almostequal.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/unittest_almostequal.py\n",
    "import unittest\n",
    "\n",
    "class AlmostEqualTest(unittest.TestCase):\n",
    "\n",
    "    def test_NotAlmostEqual(self):\n",
    "        self.assertNotAlmostEqual(1.11, 1.3, places=1)\n",
    "\n",
    "    def test_AlmostEqual(self):\n",
    "       # self.assertAlmostEqual(1.1, 1.3, places=0)\n",
    "        self.assertAlmostEqual(0.12345678, 0.12345679) \n",
    "\n",
    "    def test_AlmostEqualWithDefault(self):\n",
    "        self.assertNotAlmostEqual(1.1, 1.3)\n",
    "        \n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_almostequal.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The arguments are the values to be compared, and **the number of decimal places** to use for the test.\n",
    "\n",
    "`assertAlmostEquals()` and `assertNotAlmostEqual()`  have an optional parameter named\n",
    "\n",
    "**places** \n",
    "\n",
    "and the numbers are compared by **computing the difference rounded to number of decimal places**.\n",
    "\n",
    "default **places=7**,\n",
    "\n",
    "hence:\n",
    "\n",
    "```python\n",
    "self.assertAlmostEqual(0.5, 0.4) is False \n",
    "```\n",
    "\n",
    "```python\n",
    "self.assertAlmostEqual(0.12345678, 0.12345679) is True.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test Fixtures\n",
    "\n",
    "<b>Fixtures are resources needed by a test</b> \n",
    "\n",
    "* if you are writing several tests for the same class, those tests all need **an instance of that class** to use for testing. \n",
    "\n",
    "\n",
    "* test fixtures include `database` connections and temporary `files ` (many people would argue that using external resources makes such tests not “unit” tests, but they are still tests and still useful). \n",
    "\n",
    "**`TestCase`** includes a special hook to **configure** and **clean up** any fixtures needed by your tests.\n",
    "\n",
    "* To configure the `fixtures`, override `setUp()`.\n",
    "\n",
    "* To clean up, override `tearDown()`.\n",
    "\n",
    "### setUp()\n",
    "\n",
    "Method called to `prepare` the test fixture. This is called immediately `before` calling `the test method`; \n",
    "\n",
    "### tearDown()\n",
    "\n",
    "Method called immediately `after` the test method has been called and the result recorded.\n",
    "\n",
    "This is `called` even if the test method `raised an exception`, so the implementation in subclasses may need to be particularly careful about checking internal state.\n",
    "\n",
    "Any exception, other than `AssertionError` or `SkipTest,` raised by this method will be considered an `error` rather than a test failure. \n",
    "\n",
    "This method will only be called if the `setUp()` succeeds,  whether the test method succeeded or not.\n",
    "\n",
    "\n",
    "* automatically call `setUp()`  and `tearDown()` \n",
    "\n",
    "   The testing framework will automatically call `setUp()`  and `tearDown()` for **every single test** we run.\n",
    "\n",
    "* any `exception` raised by this `setUp()`  and `tearDown()`  will be considered an `error` rather than a test failure. \n",
    "\n",
    "Such a working environment for the testing code is called a `fixture`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./code/unittest/unittest_fixtures.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/unittest_fixtures.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class FixturesTest(unittest.TestCase):\n",
    "\n",
    "    def setUp(self):\n",
    "        print('In setUp()')\n",
    "        self.fixture = range(1, 10)\n",
    "\n",
    "    def tearDown(self):\n",
    "        print('In tearDown()')\n",
    "        del self.fixture\n",
    "\n",
    "    def test_fixture1(self):\n",
    "        print('in test1()')\n",
    "        self.assertEqual(self.fixture, range(1, 10))\n",
    "     \n",
    "    def test_fixture2(self):\n",
    "        print('in test2()')\n",
    "        self.assertEqual(self.fixture, range(2, 10))\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When this sample test is run, you can see \n",
    "\n",
    "**the order of execution** of the `fixture` and `test` methods:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_fixtures.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Examples : Any `exception` raised by this `setUp()` or `tearDown()`\n",
    "\n",
    "* This `tearDown()` method will **only** be called if the `setUp()` succeeds,  whether the test method succeeded or not.\n",
    "\n",
    "* Any `exception` raised by this `setUp()`  and `tearDown()`  will be considered an **error** rather than **a test failure.** "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./code/unittest/unittest_fixtures_exception.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/unittest_fixtures_exception.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class FixturesTest(unittest.TestCase):\n",
    "\n",
    "    def setUp(self):\n",
    "        print('In setUp()')\n",
    "        r=1/0\n",
    "        self.fixture = range(1, 10)\n",
    "\n",
    "    def tearDown(self):\n",
    "        print('In tearDown()')\n",
    "        r=1/0\n",
    "        del self.fixture\n",
    "\n",
    "    def test_fixture1(self):\n",
    "        print('in test1()')\n",
    "        self.assertEqual(self.fixture, range(1, 10))\n",
    "     \n",
    "    def test_fixture2(self):\n",
    "        print('in test2()')\n",
    "        self.assertEqual(self.fixture, range(2, 10))\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/unittest_fixtures_exception.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test Suites\n",
    "\n",
    "**Test case instances** are grouped together according to the features they test. \n",
    "\n",
    "`unittest` provides a mechanism for this: the **test suite**, represented by unittest‘s **TestSuite** class. \n",
    "\n",
    "In most cases, calling `unittest.main()` will do the right thing and collect all the module’s test cases for you, and then execute them.\n",
    "\n",
    "However, should you want to `customize` the building of your test suite, you can do it yourself:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting ./code/unittest/test_TestSuite.py\n"
     ]
    }
   ],
   "source": [
    "%%file ./code/unittest/test_TestSuite.py\n",
    "\n",
    "import unittest\n",
    "\n",
    "class EqualityTest(unittest.TestCase):\n",
    "\n",
    "    def test_Equal(self):\n",
    "        self.assertEqual(3, 3)\n",
    "\n",
    "    def test_NotEqual(self):\n",
    "        self.assertNotEqual(2, 3)   \n",
    "\n",
    "class AlmostEqualTest(unittest.TestCase):\n",
    "\n",
    "    def test_NotAlmostEqual(self):\n",
    "        self.assertNotAlmostEqual(1.2, 1.1, places=1)\n",
    "\n",
    "    def test_AlmostEqual(self):\n",
    "        self.assertAlmostEqual(1.1, 1.3, places=0)\n",
    "\n",
    "    def test_AlmostEqualWithDefault(self):\n",
    "        self.assertNotAlmostEqual(1.1, 1.3)\n",
    "        \n",
    "def suiteEqual():\n",
    "    suite = unittest.TestSuite()\n",
    "    suite.addTest(EqualityTest('test_Equal'))\n",
    "    suite.addTest(AlmostEqualTest('test_AlmostEqual'))\n",
    "    return suite\n",
    "\n",
    "def suiteNotEqual():\n",
    "    suite = unittest.TestSuite()\n",
    "    suite.addTest(EqualityTest('test_NotEqual'))\n",
    "    suite.addTest(AlmostEqualTest('test_NotAlmostEqual'))\n",
    "    return suite\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main(defaultTest = 'suiteNotEqual')\n",
    "    #unittest.main(defaultTest = 'suiteEqual')\n",
    "  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run ./code/unittest/test_TestSuite.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Placing the test code in a `separate` module\n",
    "\n",
    "You can place <b>the definitions of test cases</b> and <b>test suites</b> in the same modules as the code they are to test (such as <b>widget.py</b>),\n",
    "\n",
    "but there are several advantages to placing the `test code` in a **separate** module, such as **test_*widget*.py**:\n",
    "\n",
    "* The test module can be run standalone from the command line.\n",
    "\n",
    "* The test code can more easily be separated from shipped code.\n",
    "\n",
    "* There is less temptation to change test code to fit the code it tests without a good reason.\n",
    "\n",
    "* Test code should be modified much less frequently than the code it tests.\n",
    "\n",
    "* Tested code can be refactored more easily.\n",
    "\n",
    "* Tests for modules written in C must be in separate modules anyway, so why not be consistent?\n",
    "\n",
    "* If the testing strategy changes, there is no need to change the source code."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Further Reading\n",
    "\n",
    "### Python 3 Module of the Week\n",
    "\n",
    "https://pymotw.com/3/\n",
    "\n",
    "PyMOTW-3 is a series of articles written by Doug Hellmann to demonstrate how to use the modules of the Python 3 standard library. It is based on the original PyMOTW series, which covered Python 2.7. See About Python Module of the Week for details including the version of Python and tools used.\n",
    "\n",
    "unittest — Automated Testing Framework\n",
    "\n",
    "https://pymotw.com/3/unittest/index.html\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.7"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": false,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "165px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "widgets": {
   "state": {},
   "version": "1.1.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
